#include "ZLMonitorControlBoard.h"

#include <sstream>
#include <iostream>

#include "CDeviceManager.h"
#include "zlbase/zlbase.h"

// 异或校验
uint8_t generate_bcc_checksum(const unsigned char *buff, size_t len)
{
    if (buff == nullptr || len <= 0)
    {
        return 0x00;
    }

    uint8_t sum = 0x00;
    for (size_t i = 0; i < len; ++i)
    {
        sum ^= buff[i];
    }

    return sum;
}

// 智莱标准快件箱监控板协议V1.0.0.200901读写接口
#define COMMAND_MAX_LENGTH 512
#define COMMAND_MAX_ERRCNT 3
int32_t write_and_read(
    std::shared_ptr<zl::zlbase::IBaseDatalink> dt,
    unsigned char code,
    const unsigned char *cmd, size_t cmd_len,
    unsigned char *pkg, size_t &pkg_len)
{
    if (cmd_len > COMMAND_MAX_LENGTH - 2)
    {
        return zl::zlbase::ZLResult_WriteFailed;
    }
    size_t data_len = cmd_len + 3;
    unsigned char data[COMMAND_MAX_LENGTH] = {0x00};
    data[0] = uint8_t(data_len - 1);
    data[1] = code;
    memcpy(data + 2, cmd, cmd_len);
    data[data_len - 1] = generate_bcc_checksum(data, data_len - 1);

    // 读取链路中可能存在的脏数据
    for (int i = 0; i < COMMAND_MAX_ERRCNT; ++i)
    {
        unsigned char tmp[COMMAND_MAX_LENGTH] = {0x00};
        if (dt->Read(tmp, COMMAND_MAX_LENGTH, 50) == 0)
        {
            break;
        }
    }

    // 将指令写入链路
    auto cnt = dt->Write(data, data_len, 300);
    if (cnt != data_len)
    {
        return zl::zlbase::ZLResult_WriteTimeout;
    }

    // 读取回复
    unsigned char rep[COMMAND_MAX_LENGTH] = {0x00};
    auto rep_len = dt->Read(rep, COMMAND_MAX_LENGTH, 1000);
    if (rep_len < 3)
    {
        return zl::zlbase::ZLResult_ReadTimeout;
    }

    if (rep[0] != code)
    {
        return zl::zlbase::ZLResult_ReadFailed;
    }

    if (rep[1] == 0x81 && rep_len == 3)
    {
        return zl::zlbase::ZLResult_InvalidData;
    }

    if (generate_bcc_checksum(rep, rep_len - 1) != rep[rep_len - 1])
    {
        return zl::zlbase::ZLResult_InvalidBcc;
    }

    pkg_len = 3;
    memcpy(pkg, rep, pkg_len);
    return zl::zlbase::ZLResult_Success;
}

int32_t ZLMcbGetFirmwareVersion(char *version)
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->GetFirmwareVersion(version);
}

int32_t ZLMcbResetSystem()
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->ResetSystem();
}

int32_t ZLMcbResetDcb()
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->ResetDcb();
}

int32_t ZLMcbSetTemperatureThreshold(int32_t th, int32_t tl)
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->SetTemperatureThreshold(th, tl);
}

int32_t ZLMcbGetTemperatureThreshold(int32_t *th, int32_t *tl)
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->GetTemperatureThreshold(*th, *tl);
}

int32_t ZLMcbEnableFan(unsigned char flag)
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->EnableFan(flag);
}

int32_t ZLMcbGetFanStatus(int32_t *speed, unsigned char *status)
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->GetFanStatus(*speed, *status);
}

int32_t ZLMcbEnableLamp(unsigned char flag)
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->EnableLamp(flag);
}

int32_t ZLMcbGetLampStatus(unsigned char *status)
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->GetLampStatus(*status);
}

int32_t ZLMcbGetTemperature(int32_t *temp)
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    return device->GetTemperature(*temp);
}

// 该指令为扩展指令, 仅针对USA006项目有效
// 基础组件： 智莱标准快件箱监控板_V1.0.0200901   | V1.0.0.200901    | 2021-04-27 | F705E0ED69E363DCA6A174DE64326267


int32_t ZLMcbResetRouter()
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    auto dt = device->GetDatalink();
    if (dt == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    zl::zlbase::CAutoDatalinkLocker lk(dt);

    size_t pkg_len = COMMAND_MAX_LENGTH;
    unsigned char pkg[COMMAND_MAX_LENGTH] = {0x00};
    auto ret = write_and_read(dt, 0x29, 0x00, 0, pkg, pkg_len);
    if (ret != zl::zlbase::ZLResult_Success)
    {
        return ret;
    }

    if (pkg_len < 1 || pkg[1] != 0x00)
    {
        return zl::zlbase::ZLResult_ExecutionFailed;
    }

    return zl::zlbase::ZLResult_Success;
}

int32_t ZLMcbEnablePowerMonitoring(unsigned char flag)
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    auto dt = device->GetDatalink();
    if (dt == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    zl::zlbase::CAutoDatalinkLocker lk(dt);
    constexpr size_t cmd_len = 0x01;
    unsigned char cmd[cmd_len] = {0x00};
    cmd[0] = flag;

    size_t pkg_len = COMMAND_MAX_LENGTH;
    unsigned char pkg[COMMAND_MAX_LENGTH] = {0x00};
    auto ret = write_and_read(dt, 0x30, cmd, cmd_len, pkg, pkg_len);
    if (ret != zl::zlbase::ZLResult_Success)
    {
        return ret;
    }

    if (pkg_len < 1 || pkg[1] != 0x00)
    {
        return zl::zlbase::ZLResult_ExecutionFailed;
    }

    return zl::zlbase::ZLResult_Success;
}

int32_t ZLMcbHeartBeat()
{
    auto device = CDeviceManager::Instance().GetMonitorControlBoard();
    if (device == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    auto dt = device->GetDatalink();
    if (dt == nullptr)
    {
        return zl::zlbase::ZLResult_Unconnected;
    }

    zl::zlbase::CAutoDatalinkLocker lk(dt);

    size_t pkg_len = COMMAND_MAX_LENGTH;
    unsigned char pkg[COMMAND_MAX_LENGTH] = {0x00};
    auto ret = write_and_read(dt, 0x31, 0x00, 0, pkg, pkg_len);
    if (ret != zl::zlbase::ZLResult_Success)
    {
        return ret;
    }

    if (pkg_len < 1 || pkg[1] != 0x00)
    {
        return zl::zlbase::ZLResult_ExecutionFailed;
    }

    return zl::zlbase::ZLResult_Success;
}